﻿using BarManager.Core.ApplicationService.Interfaces;
using BarManager.Core.BusinessRules.Interfaces.Validator;
using BarManager.Core.DomainModel;
using BarManager.Core.DomainService;
using BarManager.Core.Entity;
using System;
using System.Collections.Generic;
using System.Linq;

namespace BarManager.Core.ApplicationService
{
    public class EnvioService : BaseApplicationService<Envio>, IEnvioService
    {
        private IEnvioRepository Repository
        {
            get { return (IEnvioRepository)this.BaseRepository; }
        }
        private IEnvioDetalleRepository EnvioDetalleRepository { get; set; }
        private IProductoService ProductoService { get; set; }

        public EnvioService(IObjectContext context, IEnvioRepository repository, IProductoService productoService,
             IEnvioDetalleRepository envioDetalleRepository, IEnvioValidator validator)
            : base(context, repository, validator)
        {

            this.ProductoService = productoService;
            this.EnvioDetalleRepository = envioDetalleRepository;
        }

        public override IEnumerable<string> Add(Envio entity)
        {
            if (Validator.IsValid(entity, ValidatorType.Add))
            {
                using (var tx = UnitOfWork.StartTransaction())
                {                  
                    //- Descontar productos de stock
                    ProductoService.Stock(entity.Detalle, MovimientoStock.Descontar);

                    //- Guardar Orden
                    Repository.Add(entity);

                    //- Commit
                    UnitOfWork.Commit();
                    tx.Complete();
                }
            }
            return Validator.BrokenRules();
        }

        public override IEnumerable<string> Update(Envio entity)
        {
            if (Validator.IsValid(entity, ValidatorType.Update))
            {
                using (var tx = UnitOfWork.StartTransaction())
                {
                    #region Actualizamos cabecera
                    Envio envio = Repository.Get(x => x.Id == entity.Id);
                    envio.ClienteId = entity.ClienteId;
                    envio.DomicilioId = entity.DomicilioId;
                    envio.Estado = entity.Estado;
                    envio.HoraEnvio = entity.HoraEnvio;
                    envio.Paga = entity.Paga;
                    envio.Recargo = entity.Recargo;
                    #endregion                   

                    #region Actualizamos detalle
                    //- Buscamos detalle viejos en BD
                    var list = EnvioDetalleRepository.Find(x => x.EnvioId == entity.Id);

                    //- Agregamos o actualizamos items
                    foreach (var item in entity.Detalle)
                    {
                        //- Buscamos si existe el item en BD
                        var aux = list.FirstOrDefault(x => x.Guid == item.Guid);

                        //- Si existe y no son iguales actualiza
                        if (aux != null && !EqualsItems(aux, item)) UpdateDetalle(aux, item);

                        //- Si es null es nuevo item
                        if (aux == null)
                        {
                            //- Seteamos a que orden pertenece el nuevo item
                            item.EnvioId = entity.Id;
                            AddDetalle(item);
                        }
                    }

                    //- Eliminamos items que fueron quitados del detalle
                    foreach (var item in list)
                    {
                        //- Buscamos si existe el item en memoria
                        var aux = entity.Detalle.FirstOrDefault(x => x.Guid == item.Guid);

                        //- Si es null es un item a eliminar
                        if (aux == null) RemoveDetalle(item);
                    }
                    #endregion

                    //- Commit
                    UnitOfWork.Commit();
                    tx.Complete();
                }
            }
            return Validator.BrokenRules();
        }

        public void Invoice(long id)
        {
            using (var tx = UnitOfWork.StartTransaction())
            {
                //- Obtenemos la Orden a facturar y facturamos
                Envio envio = Repository.Get(x => x.Id == id);
                envio.Estado = EstadoEnvio.Entregado;      
                                          
                //- Commit
                UnitOfWork.Commit();
                tx.Complete();
            }

        }

        public void Cancel(long id)
        {
            using (var tx = UnitOfWork.StartTransaction())
            {
                //- Obtenemos la Orden a cancelar y cancelamos
                Envio envio = Repository.Get(x => x.Id == id);
                envio.Estado = EstadoEnvio.Cancelado;
                                
                //- Roponemos Stock               
                ProductoService.Stock(envio.Detalle, MovimientoStock.Agregar);
                
                //- Commit
                UnitOfWork.Commit();
                tx.Complete();
            }

        }

        private void AddDetalle(EnvioDetalleBase item)
        {
            ProductoService.Stock(item, item.Cantidad, MovimientoStock.Descontar);
            if (item is EnvioDetalleProducto) (item as EnvioDetalleProducto).Producto = null;
            EnvioDetalleRepository.Add(item);
        }

        private void UpdateDetalle(EnvioDetalleBase itemOld, EnvioDetalleBase itemNew)
        {
            if (itemOld.Cantidad > itemNew.Cantidad)
                ProductoService.Stock(itemOld, itemOld.Cantidad - itemNew.Cantidad, MovimientoStock.Agregar);
            else if (itemOld.Cantidad < itemNew.Cantidad)
                ProductoService.Stock(itemOld, itemNew.Cantidad - itemOld.Cantidad, MovimientoStock.Descontar);

            itemOld.Cantidad = itemNew.Cantidad;
            EnvioDetalleRepository.Update(itemOld);
        }

        private void RemoveDetalle(EnvioDetalleBase item)
        {
            ProductoService.Stock(item, item.Cantidad, MovimientoStock.Agregar);
            EnvioDetalleRepository.Delete(item);
        }

        private bool EqualsItems(EnvioDetalleBase itemA, EnvioDetalleBase itemB)
        {
            return (itemA.Cantidad == itemB.Cantidad);
        }
    }
}
